#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <ctype.h>
#include <arpa/inet.h>


#include "31wrap.h"

#define MAXLINE 8192
#define SERV_PORT 8000

struct s_info {
	struct sockaddr_in cliaddr;
	int connfd;
};

void *do_work(void *arg)
{
	int n,i;
	struct s_info *ts = (struct s_info*)arg;
	char buf[MAXLINE];
	char str[INET_ADDRSTRLEN];   //#define INET_ADDRSTRLEN 16 ,use "[+d" find.

	while (1) {
		n = Read(ts->connfd, buf, MAXLINE);  // read client
		if (n == 0) {
			printf("the client %d closed...\n", ts->connfd);
			break;  // skip loop and close cfd
		}
		printf("received from %s at PORT %d\n", inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)), ntohs((*ts).cliaddr.sin_port)); // print client info
		
		for (i = 0; i < n; i++)
			buf[i] = toupper(buf[i]);  // lowwer to upper

		Write(STDOUT_FILENO, buf, n);  // write to screen
		Write(ts->connfd, buf, n);    // write to client
	}
	Close(ts->connfd);

	return (void *)0;   // Be equivalent here to pthread_exit(0);

}



//void sys_err (const char *str){
//	perror(str);
//	exit(1);
//}



int main(int argc, char *argv[]){

	struct sockaddr_in servaddr, cliaddr;
	socklen_t cliaddr_len;
	int listenfd, connfd;
	pthread_t tid;

	struct s_info ts[256];  // create struct array;
	int i = 0;

	listenfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));   // The address structure is cleared
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(SERV_PORT);

	Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));  // bind

	Listen(listenfd, 128);   // Set the maximum number of connections to a service at a time

	printf("Accepting client connect ...\n");

	while (1) {
		cliaddr_len = sizeof(cliaddr);
		connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);  // blocking the listening client connection

		ts[i].cliaddr = cliaddr;
		ts[i].connfd = connfd;

		pthread_create(&tid, NULL, do_work, (void *)&ts[i]);
		pthread_detach(tid);   // child threads are separated to prevent zombie threads

		i++;
		
	}


	return 0;
}
